#include "mf_i2c.h"

/**
 * @brief mf_i2c_send_bit
 * @param I2C_BIT_DS
 * @return FL_ErrorStatus (1 or 0)
 */
static FL_ErrorStatus mf_i2c_send_bit(MF_I2C_BIT_DS param)
{
    uint32_t timeout_ctr = 0;
    bool start_status, stop_status;

    switch (param)
    {
    case I2C_START_DS:
        FL_I2C_Master_EnableI2CStart(MF_I2C_BASE);
        do
        {
            start_status = FL_I2C_Master_IsActiveFlag_Start(MF_I2C_BASE);
            timeout_ctr++;
        } while ((start_status != FL_PASS) && (timeout_ctr != MF_I2C_TIMEOUT));
        return FL_PASS;

    case I2C_RESTART_DS:
        FL_I2C_Master_EnableI2CRestart(MF_I2C_BASE);
        do
        {
            start_status = FL_I2C_Master_IsActiveFlag_Start(MF_I2C_BASE);
            timeout_ctr++;
        } while ((start_status != FL_PASS) && (timeout_ctr != MF_I2C_TIMEOUT));
        return FL_PASS;

    case I2C_STOP_DS:
        FL_I2C_Master_EnableI2CStop(MF_I2C_BASE);
        do
        {
            stop_status = FL_I2C_Master_IsActiveFlag_Stop(MF_I2C_BASE);
            timeout_ctr++;
        } while ((stop_status != FL_PASS) && (timeout_ctr != MF_I2C_TIMEOUT));
        return FL_PASS;

    default:
        return FL_FAIL;
    }
}

/**
 * @brief mf_i2c_send_byte
 * @param data
 * @return FL_ErrorStatus (1 or 0)
 */
static FL_ErrorStatus mf_i2c_send_byte(uint8_t data)
{
    FL_I2C_Master_WriteTXBuff(MF_I2C_BASE, data);

    while (FL_I2C_Master_IsActiveFlag_TXComplete(MF_I2C_BASE) != FL_PASS)
        ;

    FL_I2C_Master_ClearFlag_TXComplete(MF_I2C_BASE);

    if (FL_I2C_Master_IsActiveFlag_NACK(MF_I2C_BASE) != FL_PASS)
    {
        return FL_PASS;
    }
    else
    {
        FL_I2C_Master_ClearFlag_NACK(MF_I2C_BASE); /* NACK is ok -> clear NACK bit */
        return FL_FAIL;
    }
}

/**
 * @brief mf_i2c_receive_byte
 * @param *data
 * @return FL_ErrorStatus (1 or 0)
 */
static FL_ErrorStatus mf_i2c_receive_byte(uint8_t *pdata)
{
    FL_I2C_Master_EnableRX(MF_I2C_BASE);

    while (FL_I2C_Master_IsActiveFlag_RXComplete(MF_I2C_BASE) != FL_PASS) /* wait rd_IT */
        ;

    FL_I2C_Master_ClearFlag_RXComplete(MF_I2C_BASE);
    *pdata = FL_I2C_Master_ReadRXBuff(MF_I2C_BASE);

    return FL_PASS;
}

/**
 * @brief mf_i2c_send_addr
 * @param dev_addr
 * @param opt
 * @param reg_addr
 * @param reg_addr_len
 * @return FL_ErrorStatus (1 or 0)
 */
static FL_ErrorStatus mf_i2c_send_addr(uint8_t dev_addr, MF_I2C_OPTION opt, uint16_t reg_addr, MF_I2C_REG_ADDR_LEN reg_addr_len)
{
    bool ret = FL_PASS;
    /* send i2c dev_addr start */
    ret |= mf_i2c_send_bit(I2C_START_DS); /* send start bit */
    if (ret != FL_PASS)
    {
        return FL_FAIL;
    }
    FL_I2C_Master_DisableRX(MF_I2C_BASE); /* disable i2c rd */
    ret |= mf_i2c_send_byte(dev_addr);    /* send device addr and option */
    if (ret != FL_PASS)
    {
        return FL_FAIL;
    }
    /* send i2c dev_addr ends */

    /* send i2c reg_addr start */
    if (reg_addr_len == I2C_REG_OVER_ADDR)
    {
        ret |= mf_i2c_send_byte(reg_addr >> 8); /* send reg_addr H */
        if (ret != FL_PASS)
        {
            return FL_FAIL;
        }
    }
    ret |= mf_i2c_send_byte(reg_addr); /* send reg_addr L */
    if (ret != FL_PASS)
    {
        return FL_FAIL;
    }
    /* send i2c reg_addr end */

    /* check opt is wr/rd start */
    if (opt == I2C_MASTER_READ)
    {
        ret |= mf_i2c_send_bit(I2C_RESTART_DS); /* send restart bit */
        if (ret != FL_PASS)
        {
            return FL_FAIL;
        }
        ret |= mf_i2c_send_byte(dev_addr | 1); /* send device addr and option */
        if (ret != FL_PASS)
        {
            return FL_FAIL;
        }
    }
    /* check opt is wr/rd start */
    return ret;
}

/**
 * @brief mf_i2c_wait_end
 * @param dev_addr
 * @return FL_ErrorStatus (1 or 0)
 */
static FL_ErrorStatus mf_i2c_wait_end(uint8_t dev_addr)
{
    bool ret = FL_PASS;
    uint32_t timeout_ctr = 0;
    do
    {
        ret = !FL_I2C_Master_IsActiveFlag_Busy(MF_I2C_BASE); /* wait write end */
        timeout_ctr++;
    } while ((ret == FL_SET) && (timeout_ctr != MF_I2C_TIMEOUT)); /* wait i2c end. i2c_is_end->ret=FL_FAIL */

    return ret;
}

/**
 * @brief mf_i2c_master_write_to_slaver
 * @param dev_addr 7bit address
 * @param reg_addr 16bit reg_address
 * @param reg_addr_len I2C_REG_BYTE_ADDR or I2C_REG_OVER_ADDR
 * @param *pdata
 * @param data_len
 * @return FL_ErrorStatus
 */
FL_ErrorStatus mf_i2c_master_write_to_device(uint8_t dev_addr, uint16_t reg_addr, MF_I2C_REG_ADDR_LEN reg_addr_len, uint8_t *pdata, uint8_t data_len)
{
    bool ret;
    uint8_t i, j;

    if (data_len > 128) /* max_option -> 128 */
    {
        return FL_FAIL;
    }

    for (i = 0; i < 3; i++)
    {
        ret = FL_PASS;
        if (!mf_i2c_send_addr(dev_addr << 1, I2C_MASTER_WRITE, reg_addr, reg_addr_len))
        {
            ret = FL_FAIL;
        }
        else
        {
            for (j = 0; j < data_len; j++)
            {
                if (!mf_i2c_send_byte(pdata[j])) /* check write is ok? */
                {
                    ret = FL_FAIL;
                    break;
                }
            }
        }
        if (!mf_i2c_send_bit(I2C_STOP_DS)) /* send stop_bit */
        {
            ret = FL_FAIL;
        }
        if (ret == FL_PASS)
        {
            ret = mf_i2c_wait_end(dev_addr << 1);
            break;
        }
    }
    return ret;
}

/**
 * @brief mf_i2c_master_read_from_slaver
 * @param dev_addr 7bit address
 * @param reg_addr 16bit reg_address
 * @param reg_addr_len I2C_REG_BYTE_ADDR or I2C_REG_OVER_ADDR
 * @param *pdata
 * @param data_len
 * @return FL_ErrorStatus
 */
FL_ErrorStatus mf_i2c_master_read_from_device(uint8_t dev_addr, uint16_t reg_addr, MF_I2C_REG_ADDR_LEN reg_addr_len, uint8_t *pdata, uint8_t data_len)
{
    bool ret;
    uint8_t i, j;

    if (data_len > 128) /* max_option -> 128 */
    {
        return FL_FAIL;
    }

    for (i = 0; i < 3; i++)
    {
        ret = FL_PASS;
        if (!mf_i2c_send_addr(dev_addr << 1, I2C_MASTER_READ, reg_addr, reg_addr_len))
        {
            ret = FL_FAIL;
        }
        else
        {
            for (j = 0; j < data_len; j++)
            {
                if (j < (data_len - 1)) /* check is? end_byte */
                {
                    FL_I2C_Master_SetRespond(MF_I2C_BASE, FL_I2C_MASTER_RESPOND_ACK);
                }
                else
                {
                    FL_I2C_Master_SetRespond(MF_I2C_BASE, FL_I2C_MASTER_RESPOND_NACK);
                }

                if (!mf_i2c_receive_byte(pdata + j)) /* get one byte */
                {
                    ret = FL_FAIL;
                    break;
                }
            }
        }
        if (!mf_i2c_send_bit(I2C_STOP_DS)) /* send stop_bit */
        {
            ret = FL_FAIL;
        }
        if (ret == FL_PASS)
        {
            break;
        }
    }

    return ret;
}

/**
 * @brief mf_i2c_master_init
 *
 * @param *I2Cx
 * @param *i2c_clk_gpiox
 * @param i2c_clk_pin
 * @param *i2c_sda_gpiox
 * @param i2c_sda_pin
 * @param baud_rate Hz
 * @return None
 */
void mf_i2c_master_init(I2C_Type *I2Cx, GPIO_Type *i2c_clk_gpiox, uint32_t i2c_clk_pin, GPIO_Type *i2c_sda_gpiox, uint32_t i2c_sda_pin, uint32_t baud_rate)
{
    FL_GPIO_InitTypeDef GPIO_InitStruct;
    FL_I2C_MasterMode_InitTypeDef I2C_InitStruct;

    /* I2C_SDA Pin init start */
    GPIO_InitStruct.pin = i2c_sda_pin;
    GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;
    GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.pull = FL_DISABLE;
    GPIO_InitStruct.remapPin = FL_ENABLE;
    FL_GPIO_Init(i2c_sda_gpiox, &GPIO_InitStruct);
    /* I2C_SDA Pin init end */

    /* PC5 I2C_SCL Pin init start */
    GPIO_InitStruct.pin = i2c_clk_pin;
    GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;
    GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.pull = FL_DISABLE;
    GPIO_InitStruct.remapPin = FL_ENABLE;
    FL_GPIO_Init(i2c_clk_gpiox, &GPIO_InitStruct);
    /* I2C_SCL Pin init end */

    /* init i2c start */
    I2C_InitStruct.clockSource = FL_RCC_I2C_CLK_SOURCE_APB1CLK;
    I2C_InitStruct.baudRate = baud_rate;
    FL_I2C_MasterMode_Init(I2Cx, &I2C_InitStruct);
    /* init i2c end */
}
